package com.flycms.web.file;

import com.flycms.core.entity.RestDataVo;
import com.flycms.module.file.model.FlyFile;
import com.flycms.module.file.service.FileService;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.util.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;

@RestController
@RequestMapping("/file")
@Slf4j
public class FileController {

    @Autowired
    private FileService fileService;

    /**
     * #文件路徑
     * custom:
     * files:
     * root: data/upload/file
     * service: data/upload/file
     */
    @Value("${file-upload.linux}")
    private String LINUX_ROOT;

    @Value("${file-upload.windows}")
    private String WINDOWS_ROOT;

    private String WINDOWS = "WINDOWS";

    private String LINUX = "LINUX";

    /**
     * 文件名最大长度
     */
    private int FILENAME_MAX_SIZE = 50;

    /**
     *
     */
    @PostMapping(value = {"/system/uploadImage","/u/uploadImage","/system/urlUpload","/u/urlUpload"})
    public RestDataVo uploadImage(MultipartHttpServletRequest request,
                                  @RequestParam(value = "ckCsrfToken" , required = false)String ckCsrfToken) {
        log.info("mall image upload begin!");
        Map<String, MultipartFile> multipartFileMap = request.getFileMap();
        if (CollectionUtils.isEmpty(multipartFileMap)) {
            log.info("files is null");
            return RestDataVo.ERROR("文件为空");
        }
        long time = System.currentTimeMillis();
        File rootFile;
        String path = this.getPathByOs();
        if (StringUtils.isBlank(path)) {
            log.info("init file Path error");
            return RestDataVo.ERROR("初始化文件路径出错");
        }
        log.info("mall activity file upload URL-->ROOT:{}", path);
        rootFile = new File(path);
        if (!rootFile.exists()) {
            rootFile.mkdirs();
        }
        RestDataVo restDataVo;
        try {
            int port=request.getServerPort();
            String portStr="";
            if(port>0){
                portStr+=":"+port;
            }
            String requestPart = "http://"+ request.getServerName() + portStr + "/file/openImage/";
            restDataVo = this.uploadFileToPath(multipartFileMap, path,requestPart);
            time = System.currentTimeMillis() - time;
            log.info("uploadFile spend:" + time + "ms");
        } catch (Exception e) {
            log.error("upload file failed.", e);
            return RestDataVo.ERROR("系统异常");
        }
        return restDataVo;
    }


    /**
     * 文件上传
     *
     * @param request 请求体
     * @return 返还结果
     */
    @PostMapping(value = {"/system/upload","/u/upload"})
    public RestDataVo uploadFile(MultipartHttpServletRequest request) {
        log.info("mall activity file upload begin!");
        Map<String, MultipartFile> multipartFileMap = request.getFileMap();
        if (CollectionUtils.isEmpty(multipartFileMap)) {
            log.info("files is null");
            return RestDataVo.ERROR("文件为空");
        }
        long time = System.currentTimeMillis();
        File rootFile;
        String path = this.getPathByOs();
        if (StringUtils.isBlank(path)) {
            log.info("init file Path error");
            return RestDataVo.ERROR("初始化文件路径出错");
        }
        log.info("mall activity file upload URL-->ROOT:{}", path);
        rootFile = new File(path);
        if (!rootFile.exists()) {
            rootFile.mkdirs();
        }
        RestDataVo restDataVo;
        try {
            int port=request.getServerPort();
            String portstr="";
            if(port>0){
                portstr+=":"+port;
            }
            restDataVo = this.uploadFileToPath(multipartFileMap, path);
            time = System.currentTimeMillis() - time;
            log.info("uploadFile spend:" + time + "ms");
        } catch (Exception e) {
            log.error("upload file failed.", e);
            return RestDataVo.ERROR("系统异常");
        }
        return restDataVo;
    }

    //根据操作系统获取路径
    private String getPathByOs() {
        String path = "";
        String osName = System.getProperty("os.name");
        if (osName.toUpperCase().startsWith(WINDOWS)) {
            path = WINDOWS_ROOT;
        } else if (osName.toUpperCase().startsWith(LINUX)) {
            path = LINUX_ROOT;
        } else {
            log.error("os name can not use!");
        }
        return path;
    }

    /**
     * 文件下载
     *
     * @param name     文件在操作系统中保存的名称
     * @param response 流
     * @return 返回流
     */
    @GetMapping("/u/download/{name}")
    public void download(@PathVariable("name") String name, HttpServletResponse response) throws IOException {
        // 取得文件名。
        FlyFile flyFile = fileService.getNameByUrl(name);
        if (Objects.isNull(flyFile)) {
            log.error(" cloud not found file! name: " + name);
            return;
        }
        String path = getPathByOs() + name + flyFile.getType();
        // path是下载的文件的路径。
        File file = new File(path);
        //设置文件ContentType类型，这样设置，会自动判断下载文件类型  
        response.setContentType("multipart/form-data");
        //设置编码格式
        response.setCharacterEncoding("UTF-8");
        //设置可以识别Html文件  
        response.setContentType("text/html");
        // 2.设置文件头：最后一个参数是设置下载文件名  
        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", "attachment;filename="
                + new String(flyFile.getFileName().getBytes("utf-8"), "ISO-8859-1"));
        //（这里  可以设置成excel格式 ：response.setHeader("Content-Disposition", "attachment;fileName=" + “文件名” + ".xsl");
        OutputStream out = response.getOutputStream();
        FileInputStream fileInput = new FileInputStream(file);
        try {
            out = response.getOutputStream();
            int b = 0;
            byte[] buffer = new byte[1024];
            while ((b = fileInput.read(buffer)) != -1) {
                // 4.写到输出流(out)中  
                out.write(buffer, 0, b);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            fileInput.close();
            out.flush();
            out.close();
        }
    }

    /**
     * 打开照片
     * @param name
     * @param response
     * @throws IOException
     */
    @GetMapping("/openImage/{name}")
    public void openImage(@PathVariable("name") String name, HttpServletResponse response) throws IOException {
        // 取得文件名。
        FlyFile flyFile = fileService.getNameByUrl(name);
        if (Objects.isNull(flyFile)) {
            log.error(" cloud not found file! name: " + name);
            return;
        }
        if (!isImage(flyFile)) {
            log.error(" is not image! name: " + name);
            return;
        }
        String path = getPathByOs() + name + flyFile.getType();
        // path是下载的文件的路径。
        File file = new File(path);
        //设置文件ContentType类型，设置为图片 
        response.setContentType("image/jpeg");
        OutputStream out = response.getOutputStream();
        FileInputStream fileInput = new FileInputStream(file);
        try {
            byte[] b = new byte[1024];
            while (fileInput.read(b) != -1) {
                out.write(b);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            fileInput.close();
            out.flush();
            out.close();
        }
    }

    private boolean isImage(FlyFile flyFile) {
        String type = flyFile.getType();
        return type.equals(".jpg")
                || type.equals(".jpeg")
                || type.equals(".png");
    }

    /**
     * 文件上传
     *
     * @param fileMap 文件
     * @return 返回文件路径
     */
    private RestDataVo uploadFileToPath(Map<String, MultipartFile> fileMap, String uploadFileDir) {
        Map<String, Object> result = Maps.newHashMap();
        try {
            for (Map.Entry<String, MultipartFile> fileEntry : fileMap.entrySet()) {
                MultipartFile multipartFile = fileEntry.getValue();
                String originalFilename = multipartFile.getOriginalFilename();
                log.info("mall activity upload file name:{}", originalFilename);
                //文件类型
                String fileType = originalFilename.substring(originalFilename.lastIndexOf("."));
                String fileName = UUID.randomUUID().toString();
                //判断文件名
                if (StringUtils.isBlank(originalFilename)) {
                    log.info("originalFilename is blank!");
                    return RestDataVo.ERROR("上传的文件名为空");
                }
                if (originalFilename.length() > FILENAME_MAX_SIZE) {
                    return RestDataVo.ERROR("文件名称过长");
                }
                this.saveFile(uploadFileDir, multipartFile, fileName + fileType);
                result.put("url", fileName);
                result.put("type", fileType);
                result.put(multipartFile.getName(), originalFilename);
                //保存到数据库
                FlyFile file = FlyFile.builder()
                        .fileName(originalFilename)
                        .url(fileName)
                        .type(fileType)
                        .build();
                fileService.saveFileMsg(file);
            }
        } catch (Exception e) {
            log.error("保存文件异常:", e);
            return RestDataVo.ERROR("保存文件异常");
        }
        return RestDataVo.OK(result);
    }

    /**
     * 图片上传
     *
     * @param fileMap 文件
     * @return 返回文件路径
     */
    private RestDataVo uploadFileToPath(Map<String, MultipartFile> fileMap, String uploadFileDir,String requestPart) {
        Map<String, Object> result = Maps.newHashMap();
        try {
            for (Map.Entry<String, MultipartFile> fileEntry : fileMap.entrySet()) {
                MultipartFile multipartFile = fileEntry.getValue();
                String originalFilename = multipartFile.getOriginalFilename();
                log.info("mall image file name:{}", originalFilename);
                //文件类型
                String fileType = originalFilename.substring(originalFilename.lastIndexOf("."));
                boolean isImg = fileType.equals(".jpg") || fileType.equals(".jpeg") || fileType.equals(".png");
                if(!isImg){
                    return RestDataVo.ERROR("非图片文件不允许上传,请上传后缀为 jpg|jpeg|png 的文件");
                }
                if (multipartFile.getSize() > 1024 * 1024 * 2) {
                    return RestDataVo.ERROR("图片大小不得大于2M");
                }
                String fileName = UUID.randomUUID().toString();
                //判断文件名
                if (StringUtils.isBlank(originalFilename)) {
                    log.info("originalFilename is blank!");
                    return RestDataVo.ERROR("上传的文件名为空");
                }
                if (originalFilename.length() > FILENAME_MAX_SIZE) {
                    return RestDataVo.ERROR("文件名称过长");
                }
                this.saveFile(uploadFileDir, multipartFile, fileName + fileType);
                result.put("openUrl", requestPart + fileName);
                //保存到数据库
                FlyFile file = FlyFile.builder()
                        .fileName(originalFilename)
                        .url(fileName)
                        .type(fileType)
                        .build();
                fileService.saveFileMsg(file);
            }
        } catch (Exception e) {
            log.error("保存文件异常:", e);
            return RestDataVo.ERROR("保存文件异常");
        }
        return RestDataVo.OK(result);
    }

    /**
     * 保存文件
     *
     * @param uploadFileDir 保存路径
     * @param multipartFile 文件
     * @param fileName      文件名
     * @throws IOException IO异常
     */
    private void saveFile(String uploadFileDir, MultipartFile multipartFile, String fileName) throws IOException {
        File needSaveFile = new File(uploadFileDir + fileName);
        if (needSaveFile.exists()) {
            log.warn("The file {} already exists and will be overridden！", uploadFileDir + fileName);
        }
        FileUtils.copyToFile(multipartFile.getInputStream(), needSaveFile);
        log.info(fileName + " upload success; New File:"
                + fileName + "; File Size:" + multipartFile.getSize());
    }

}
